<?php

class CookieManager {
    private static $instance;

    /**
     * Returns the value of a cookie in memory. The advantage to using this over
     * $_COOKIE[] is the fact that this will return the right type as well.
     * @param string $cookieName
     * @return mixed A value that the cookie holds
     */
    public function getCookieValue($cookieName) {
        if (!isset($_COOKIE[$cookieName]))
            return NULL;
        $rawValue = $_COOKIE[$cookieName];
        $rawValue = $this->splitTypeFromString($rawValue);
        return $rawValue;
    }

    /**
     * Set a value to a cookie. An advantage of this method is the support for
     * native types.
     * @param string $cookieName The name of the cookie
     * @param mixed $cookieValue A value that the cookie will hold (Supports arrays as well)
     * @param integer $expiration An integral value amount of seconds since epoch that the cookie will expire
     */
    public function setCookieValue($cookieName, $cookieValue, $expiration) {
        $string = $this->getString($cookieValue);
        setcookie($cookieName, $string, $expiration);
    }

    /**
     * Removes a cookie
     * @param string $cookieName The name of the cookie to remove
     */
    public function deleteCookie($cookieName) {
        setcookie($cookieName, "", time() - 3600);
    }

    private function splitTypeFromString($string) {
        $position = strrpos($string, "::type=");
        if ($position !== false) {
            $value = substr($string, 0, $position);
            $position += 7; // Move the pointer to the start of the type variable
            $type = substr($string, $position, strpos($string, "::", $position) - $position);
            $value = $this->getValue($value, $type);
            return $value;
        }
        else {
            return $string;
        }
    }

    private function getString($value) {
        $string = "NULL";
        switch ($type = gettype($value) ) {
            case "object":
            case "unkown type":
            case "resource":
                throw new RecoverableException("Data type for cookie is not a valid type - Got Type $type.");
                break;

            case "boolean":
                $string = ($value ? "true" : "false")."::type=boolean::";
                break;

            case "array":
                $cookieString = "";
                foreach ($value as $index => $tempValue) {
                    $tempString = $this->getString($tempValue);
                    $cookieString .= "$index-:-$tempValue-:-$tempString:.:";
                }
                if (strlen($cookieString) > 3)
                    $cookieString = substr($cookieString, 0, strlen($cookieString) - 3);
                $string = $cookieString . "::type=array::";
                break;

            default:
                $string = (string)$value."::type=$type::";
                break;
        }
        return $string;
    }

    private function getValue($string, $type) {
        $value = null;
        switch ($type) {
            case "integer":
                $value = intval($string);
                break;

            case "boolean":
                $value = $string == "true" ? true : false;
                break;

            case "double":
                $value = floatval($string);
                break;

            case "string":
                $value = $string;
                break;

            case "NULL":
                $value = NULL;
                break;

            case "array":
                $value = array();
                foreach (explode(":.:", $string) as $exposedElements) {
                    list ($index, $key, $typeOfArrayElement) = explode("-:-", $exposedElements);
                    $result = $this->splitTypeFromString($typeOfArrayElement);
                    $value[$index] = $result;
                }
                break;

            default:
                $value = NULL;
                break;
        }
        return $value;
    }

    /**
     * Return the singleton object of the object.
     * @return CookieManager
     */
    public static function inst() {
        if (!isset(self::$instance)) {
            $className = __CLASS__;
            self::$instance = new $className;
        }
        return self::$instance;
    }
}

?>
